fix(rename): avoid failing hard when rename is rejected by server
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>
Mon, 5 May 2025 16:40:35 +0000 (18:40 +0200)
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>
Mon, 2 Jun 2025 10:28:01 +0000 (10:28 +0000)
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
src/libsync/discoveryphase.cpp
test/testsyncmove.cpp

index 9b72732c4c11bc8c8c8718215479151b30f9ec64..7edd684675cc943a90f4e42c2dcfc7638f64e640 100644 (file)
@@ -180,11 +180,10 @@ QPair<bool, QByteArray> DiscoveryPhase::findAndCancelDeletedJob(const QString &o
             result = true;
             oldEtag = (*it)->_etag;
         } else {
-            if (!(instruction == CSYNC_INSTRUCTION_REMOVE
-                    // re-creation of virtual files count as a delete
-                    || ((*it)->_type == ItemTypeVirtualFile && instruction == CSYNC_INSTRUCTION_NEW)
-                    || ((*it)->_isRestoration && instruction == CSYNC_INSTRUCTION_NEW)))
-            {
+            if (!(instruction == CSYNC_INSTRUCTION_REMOVE ||
+                  instruction == CSYNC_INSTRUCTION_IGNORE ||
+                  ((*it)->_type == ItemTypeVirtualFile && instruction == CSYNC_INSTRUCTION_NEW) ||// re-creation of virtual files count as a delete
+                  ((*it)->_isRestoration && instruction == CSYNC_INSTRUCTION_NEW))) {
                 qCWarning(lcDiscovery) << "ENFORCE(FAILING)" << originalPath;
                 qCWarning(lcDiscovery) << "instruction == CSYNC_INSTRUCTION_REMOVE" << (instruction == CSYNC_INSTRUCTION_REMOVE);
                 qCWarning(lcDiscovery) << "((*it)->_type == ItemTypeVirtualFile && instruction == CSYNC_INSTRUCTION_NEW)"
index 246d34882f7877a624a747bba9b5f476a5109eb8..52d0051b69431904cf53d90020a22bde2993a50b 100644 (file)
@@ -1372,6 +1372,54 @@ private slots:
         QVERIFY(dbResult);
         QCOMPARE(itemsCounter, 12);
     }
+
+    void testRenameFileThatExistsInMultiplePaths()
+    {
+        FakeFolder fakeFolder{FileInfo{}};
+        QObject parent;
+
+        fakeFolder.setServerOverride([&parent](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *) -> QNetworkReply * {
+            if (op == QNetworkAccessManager::CustomOperation
+                && request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == QStringLiteral("MOVE")) {
+                return new FakeErrorReply(op, request, &parent, 507);
+            }
+            return nullptr;
+        });
+
+        fakeFolder.remoteModifier().mkdir("FolderA");
+        fakeFolder.remoteModifier().mkdir("FolderA/folderParent");
+        fakeFolder.remoteModifier().insert("FolderA/folderParent/FileA.txt");
+        fakeFolder.remoteModifier().mkdir("FolderB");
+        fakeFolder.remoteModifier().mkdir("FolderB/folderChild");
+        fakeFolder.remoteModifier().insert("FolderB/folderChild/FileA.txt");
+        fakeFolder.remoteModifier().mkdir("FolderC");
+
+        const auto fileAFileInfo = fakeFolder.remoteModifier().find("FolderB/folderChild/FileA.txt");
+        const auto fileAInFolderAFolderFileId = fileAFileInfo->fileId;
+        const auto fileAInFolderAEtag = fileAFileInfo->etag;
+        const auto duplicatedFileAFileInfo = fakeFolder.remoteModifier().find("FolderB/folderChild/FileA.txt");
+
+        duplicatedFileAFileInfo->fileId = fileAInFolderAFolderFileId;
+        duplicatedFileAFileInfo->etag = fileAInFolderAEtag;
+
+        QVERIFY(fakeFolder.syncOnce());
+
+        QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+        fakeFolder.localModifier().rename("FolderA/folderParent/FileA.txt", "FolderC/FileA.txt");
+
+        qDebug() << fakeFolder.currentLocalState();
+
+        fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::FilesystemOnly);
+        QVERIFY(!fakeFolder.syncOnce());
+
+        qDebug() << fakeFolder.currentLocalState();
+
+        fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::FilesystemOnly);
+        QVERIFY(fakeFolder.syncOnce());
+
+        qDebug() << fakeFolder.currentLocalState();
+    }
 };
 
 QTEST_GUILESS_MAIN(TestSyncMove)